﻿using Microsoft.Win32;
using Newtonsoft.Json.Linq;
using SerialPort_Assistant.Command;
using SerialPort_Assistant.Enums;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.Common;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Threading;

namespace SerialPort_Assistant.ViewModel
{
    public class MainViewModel : BaseViewModel
    {
        // 用来发送串口数据
        private System.Timers.Timer timer;
        // 用来更新状态栏时间
        private System.Timers.Timer timer2;

        private CancellationTokenSource cts = new CancellationTokenSource();
        private Task recTask;

        public string FileName { get; set; }
        public List<string> PortNames { get; set; }
        public List<int> BaudRateTypes { get; set; }
        public Dictionary<string,Parity> ParityTypes { get; set; }
        public List<int> DataBitTypes { get; set; }
        public Dictionary<string,StopBits> StopBitTypes { get; set; }
        public Dictionary<string,Encoding> EncodingTypes { get; set; }
        public List<TimeUnit> TimeUnitTypes { get; set; }

        public MainViewModel()
        {
            InitTimer();
            InitTimer2();

            OpenSerialPortCommand = new BaseCommand(OpenSerialPort, null);
            CloseSerialPortCommand = new BaseCommand(CloseSerialPort, null);
            ClearSendAreaCommand = new BaseCommand(ClearSendArea, null);
            SaveRecvAreaCommand = new BaseCommand(SaveRecvArea, null);
            SaveToNewFileCommand = new BaseCommand(SaveToNewFile, null);
            ClearRecvAreaCommand = new BaseCommand(ClearRecvArea, null);
            SendCommand = new BaseCommand(SendData, null);
            LoadDataFileCommand = new BaseCommand(LoadDataFile, null);
            CRCCalcCommand = new BaseCommand(CRCCalc, null);

            InitViewData();
        }

        private void InitTimer()
        {
            timer = new System.Timers.Timer();
            timer.AutoReset = true;
            timer.Interval = Interval * TimeUnitType;
            timer.Elapsed += Timer_Elapsed;
        }

        private void InitTimer2()
        {
            timer2 = new System.Timers.Timer();
            timer2.AutoReset = true;
            timer2.Enabled = true;
            timer2.Interval = 1000;
            timer2.Elapsed += Timer2_Elapsed;
        }

        private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            if (Application.Current?.Dispatcher != null && serialPort != null && serialPort.IsOpen)
            {
                WriteData(content);
            }
            else
            {
                timer.Stop();// UI线程销毁，定时器结束
            }
        }

        private void Timer2_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            if (Application.Current?.Dispatcher != null)
            {
                Application.Current.Dispatcher.Invoke(() =>
                {
                    CurrentTime = DateTime.Now.ToString("yyyy年MM月dd日 HH时mm分ss秒");
                });
            }
            else
            {
                timer2.Stop();// UI线程销毁，定时器停止
            }
        }

        private void OpenSerialPort(object obj)
        {
            // 判断串口对象是否创建
            serialPort = new SerialPort();
            serialPort.PortName = portName;
            serialPort.BaudRate = baudRate;
            serialPort.Parity = parity;
            serialPort.DataBits = dataBits;
            serialPort.StopBits = stopBits;

            // 设置读写超时时间
            serialPort.WriteTimeout = 5000;
            serialPort.ReadTimeout = readTimeout;

            // 打开串口
            try
            {
                if (!serialPort.IsOpen)
                {
                    serialPort.Open();
                    OpenStatus = "串口已打开";
                    StatusBackColor = "#EE7942";
                    if (recTask == null)
                    {
                        recTask = Task.Run(() =>
                        {
                            while (!cts.IsCancellationRequested)
                            {
                                if (serialPort.IsOpen && serialPort.BytesToRead > 0)
                                {
                                    byte[] data = new byte[serialPort.BytesToRead];
                                    serialPort.Read(data, 0, data.Length);

                                    StringBuilder builder = new StringBuilder();
                                    string dateTimeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                    builder.Append(dateTimeStr + "  Trx:  ");

                                    switch (showType)
                                    {
                                        case ShowTypes.ASCII:
                                            builder.Append(encodingType.GetString(data, 0, data.Length));
                                            break;
                                        case ShowTypes.Hex:
                                            builder.Append(ByteToBase(data, 16));
                                            break;
                                        case ShowTypes.Dec:
                                            builder.Append(ByteToBase(data, 10));
                                            break;
                                        case ShowTypes.Oct:
                                            builder.Append(ByteToBase(data, 8));
                                            break;
                                        case ShowTypes.Bin:
                                            builder.Append(ByteToBase(data, 2));
                                            break;
                                        default:
                                            break;
                                    }
                                    AddMsg(builder.ToString());
                                }
                                Thread.Sleep(100);
                            }

                            return Task.CompletedTask;
                        }, cts.Token);
                    }
                    MessageBox.Show("串口打开成功", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        // 转换成不同进制 
        private string ByteToBase(byte[] datas, int @base)
        {
            StringBuilder builder = new StringBuilder();

            if (16 == @base)
            {
                foreach (byte data in datas)
                {
                    builder.Append(data.ToString("X2") + " ");
                }
            }
            else if(2 == @base)
            {
                foreach (byte data in datas)
                {
                    builder.Append(Convert.ToString(data, @base).PadLeft(8,'0') + " ");
                }
            }
            else
            {
                foreach (byte data in datas)
                {
                    builder.Append(Convert.ToString(data, @base) + " ");
                }
            }
            return builder.ToString();
        }

        private void CloseSerialPort(object obj)
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                try
                {
                    serialPort.Close();
                    OpenStatus = "串口未打开";
                    StatusBackColor = "#0781d7";
                    cts.Cancel();
                    recTask = null;
                    timer.Stop();
                    MessageBox.Show("串口关闭成功", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "串口关闭提示", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void ClearSendArea(object obj)
        {
            Content = string.Empty;
        }

        private async void SaveRecvArea(object obj)
        {   
            if(msgList.Count == 0)
            {
                MessageBox.Show("当前接收区内容不能为空！");
                return;
            }

            if (string.IsNullOrEmpty(FileName))
            {
                SaveFileDialog saveFileDialog = new SaveFileDialog();
                saveFileDialog.Filter = "Text File (*.txt)|*.txt|All File (*.*)|*.*";
                saveFileDialog.AddExtension = true;
                bool? flag = saveFileDialog.ShowDialog();
                if (flag != null && flag == true)
                {
                    FileName = saveFileDialog.FileName;
                }
                else
                {
                    return;
                }
            }
                   
            try
            {  
                if (File.Exists(FileName))
                {
                    await Task.Run(() =>
                    {
                        File.AppendAllLines(FileName, msgList.ToArray(), encodingType);
                    });
                }
                else
                {
                    await Task.Run(() =>
                    {
                        File.WriteAllLines(FileName, msgList.ToArray(), encodingType);
                    });
                }
                MessageBox.Show("文件保存成功","提示", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private async void SaveToNewFile(object obj)
        {
            if (msgList.Count == 0)
            {
                MessageBox.Show("当前接收区内容不能为空！");
                return;
            }

            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "Text File (*.txt)|*.txt|All File (*.*)|*.*";
            saveFileDialog.AddExtension = true;
            bool? flag = saveFileDialog.ShowDialog();
            if (flag != null && flag == true)
            {
                FileName = saveFileDialog.FileName;
            }
            else
            {
                return;
            }

            try
            {
                if (File.Exists(FileName))
                {
                    await Task.Run(() =>
                    {
                        File.AppendAllLines(FileName, msgList.ToArray(), encodingType);
                    });
                }
                else
                {
                    await Task.Run(() =>
                    {
                        File.WriteAllLines(FileName, msgList.ToArray(), encodingType);
                    });
                }
                MessageBox.Show("文件另存成功", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void ClearRecvArea(object obj)
        {
            MsgList.Clear();
        }

        private void CRCCalc(object param)
        {
            int idx = 0;
            string[] strArr = param.ToString().Split(' ');
            byte[] buff = new byte[strArr.Length];
            foreach (string str in strArr)
            {
                buff[idx] = Convert.FromHexString(str)[0];
                idx++;
            }

            byte[] bytes = CRC16(buff);
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < bytes.Length; i++)
            {
                builder.Append(bytes[i].ToString("X2") + " ");
            }
            CRCCode = builder.ToString();
        }

        private byte[] CRC16(byte[] data)
        {
            ushort crc = 0xFFFF;

            for (int i = 0; i < data.Length; i++)
            {
                crc = (ushort)(crc ^ (data[i]));
                for (int j = 0; j < 8; j++)
                {
                    crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                }
            }

            byte hi = (byte)((crc & 0xFF00) >> 8);
            byte lo = (byte)(crc & 0x00FF);

            byte[] buff = new byte[2];
            buff[0] = lo;
            buff[1] = hi;
            return buff;
        }

        private async void WriteData(string parameter)
        {
            switch (sendType)
            {
                case SendTypes.ASCII:
                    AsciiMode(parameter);
                    break;
                case SendTypes.Hex:
                    BinaryMode(parameter);
                    break;
                default:
                    break;
            }

            string dateTimeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            string msg = dateTimeStr + "  Trx:  " + parameter;
            AddMsg(msg);
            await Task.Delay(50);
        }

        private void AsciiMode(string param)
        {
            serialPort.WriteLine(param);
        }

        private void BinaryMode(string param) 
        {
            try
            {
                int idx = 0;
                string[] strArr = param.Split(' ');
                byte[] buff = new byte[strArr.Length];
                foreach (string str in strArr)
                {
                    buff[idx] = Convert.FromHexString(str)[0];
                    idx++;
                }
                serialPort.Write(buff, 0, buff.Length);
            }
            catch (Exception ex)
            {
                //string formatStr = "01 03 00 00 00 02 4E 3C";
                MessageBox.Show(ex.Message);
                //MessageBox.Show($"数据格式错误,只能提供这种类型的数据格式：{formatStr}", "错误", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }
        private void SendData(object obj)
        {
            if (serialPort == null || serialPort.IsOpen == false)
            {
                MessageBox.Show("当前并未打开串口,请打开串口后重试", "错误提示", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (obj == null || string.IsNullOrEmpty(obj.ToString()))
            {
                MessageBox.Show("发送内容不能为空", "错误提示", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            string val = obj.ToString();

            WriteData(val);
        }
        private void LoadDataFile(object obj)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "Text File (*.txt)|*.txt|All File (*.*)|*.*";
            bool? flag = openFileDialog.ShowDialog();
            if (flag == false) return;

            List<byte> data = new List<byte>();
            using (Stream stream = openFileDialog.OpenFile())
            {
                //byte[] buff = new byte[1024];
                //while(stream.Read(buff, 0, buff.Length) > 0)
                //{
                //    data.AddRange(buff);
                //}
                ObservableCollection<string> dataList = new ObservableCollection<string>();
                using(StreamReader reader = new StreamReader(stream))
                {
                    string line;
                    while ((line = reader.ReadLine())!= null)
                    {
                        dataList.Add(line);
                    }
                }
                MsgList = dataList;
            }
        }

        public ICommand OpenSerialPortCommand { get; }

        public ICommand CloseSerialPortCommand { get; }

        public ICommand SaveRecvAreaCommand { get; }

        public ICommand SaveToNewFileCommand { get; }

        public ICommand ClearRecvAreaCommand { get; }

        public ICommand ClearSendAreaCommand { get; }

        public ICommand SendCommand { get; }

        public ICommand LoadDataFileCommand { get; }

        public ICommand CRCCalcCommand { get; }

        private string portName = "COM1";

        public string PortName
        {
            get { return portName; }
            set { 
                portName = value;
                OnPropertyChanged();
            }
        }

        private int baudRate = 9600;

        public int BaudRate
        {
            get { return baudRate; }
            set { 
                baudRate = value;
                OnPropertyChanged();
            }
        }

        private Parity parity = Parity.None;

        public Parity Parity
        {
            get { return parity; }
            set { 
                parity = value;
                OnPropertyChanged();
            }
        }

        private int dataBits = 8;

        public int  DataBits
        {
            get { return dataBits; }
            set { 
                dataBits = value;
                OnPropertyChanged();
            }
        }

        private StopBits stopBits = StopBits.One;

        public StopBits StopBits
        {
            get { return stopBits; }
            set { 
                stopBits = value;
                OnPropertyChanged();
            }
        }

        private Encoding encodingType = Encoding.UTF8;

        public Encoding EncodingType
        {
            get { return encodingType; }
            set { 
                encodingType = value;
                OnPropertyChanged();
            }
        }

        private SerialPort serialPort;

        public SerialPort SerialPort
        {
			get { return serialPort; }
			set { serialPort = value; }
		}

        private ObservableCollection<string> msgList = new ObservableCollection<string>();
        public ObservableCollection<string> MsgList
        {
            get { return msgList; }
            set { 
                msgList = value;
                OnPropertyChanged();
            }
        }

        private void AddMsg(string msg)
        {
            Application.Current.Dispatcher.Invoke(() => 
            {
                MsgList.Add(msg);
            });
        }

        private void RemoveMsg(string msg)
        {
            MsgList.Remove(msg);
        }

        private string content = "你好呀，欢迎使用自定义串口调试助手";

        public string Content
        {
            get { return content; }
            set { 
                content = value;
                OnPropertyChanged();
            }
        }

        private int interval = 1000;

        public int Interval
        {
            get { return interval; }
            set { 
                interval = value;
                OnPropertyChanged();
                if(interval < 0)
                {
                    timer.Enabled = false;
                    timer.Stop();
                }
                else
                {
                    timer.Interval = interval * timeUnitType;
                    timer.Enabled = true;
                }
            }
        }

        private int timeUnitType = 1;

        public int TimeUnitType
        {
            get { return timeUnitType; }
            set { 
                timeUnitType = value;
                OnPropertyChanged();
            }
        }

        private bool isAutoSend = false;

        public bool IsAutoSend
        {
            get { return isAutoSend; }
            set {
                isAutoSend = value;
                OnPropertyChanged();
                if (isAutoSend)
                {
                    if (serialPort == null || !serialPort.IsOpen)
                    {
                        MessageBox.Show("当前串口并未打开", "错误提示");
                        isAutoSend = false;
                        return;
                    }
                    if (interval<=0)
                    {
                        timer.Stop();
                        isAutoSend = false;
                        MessageBox.Show("当前设置的时间间隔错误", "错误提示");
                    }
                    else
                    {
                        timer.Start();
                    }
                }
                else
                {
                    timer.Stop();
                }
            }
        }

        private bool showRecv = true;

        public bool ShowRecv
        {
            get { return showRecv; }
            set { 
                showRecv = value;
                OnPropertyChanged();
            }
        }

        private ShowTypes showType = ShowTypes.ASCII;

        public ShowTypes ShowType
        {
            get { return showType; }
            set { 
                showType = value;
                OnPropertyChanged();
            }
        }

        private SendTypes sendType = SendTypes.ASCII;

        public SendTypes SendType
        {
            get { return sendType; }
            set { 
                sendType = value;
                OnPropertyChanged();
            }
        }

        private SendWays sendWay = SendWays.ByHand;

        public SendWays SendWay
        {
            get { return sendWay; }
            set { 
                sendWay = value;
                OnPropertyChanged();
                if(sendWay == SendWays.LoadFile)
                {
                    ExecuteCmdFromFile();
                }
            }
        }

        private string crcCode;

        public string CRCCode
        {
            get { return crcCode; }
            set 
            { 
                crcCode = value;
                OnPropertyChanged();
            }
        }


        private void ExecuteCmdFromFile()
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Multiselect = false;
            bool? flag = dialog.ShowDialog();
            if(flag!=null && flag == true)
            {
                string fileName = dialog.FileName;
                string data = File.ReadAllText(fileName, encodingType);
                string[] cmdList = data.Split("\r\n");
                BatchCmdExecute(cmdList);
            }
        }

        private void BatchCmdExecute(string[] cmdList)
        {
            Task.Run(() =>
            {
                for (int i = 0; i < cmdList.Length; i++)
                {
                    WriteData(cmdList[i]);
                }
            });
        }

        // 读取超时时间100毫秒
        private int readTimeout = 100;

        public int ReadTimeout
        {
            get { return readTimeout; }
            set { readTimeout = value; }
        }

        private string openStatus = "串口未打开";

        public string OpenStatus
        {
            get { return openStatus; }
            set { 
                openStatus = value;
                OnPropertyChanged();
            }
        }


        private string statusBackColor = "#0781d7";

        public string StatusBackColor
        {
            get { return statusBackColor; }
            set { 
                statusBackColor = value;
                OnPropertyChanged();
            }
        }

        private string currentTime = DateTime.Now.ToString("yyyy年MM月dd日 HH时mm分ss秒");

        public string CurrentTime
        {
            get { return currentTime; }
            set { 
                currentTime = value;
                OnPropertyChanged();
            }
        }

        #region 初始化页面数据
        private void InitViewData()
        {
            InitPortNames();
            InitTimeUnitTypes();
            InitBaudRateTypes();
            InitParityTypes();
            InitDataBitTypes();
            InitStopBitTypes();
            InitEncodingTypes();
        }

        /// <summary>
        /// 初始化端口名称
        /// </summary>
        private void InitPortNames()
        {
            string[] portNames = SerialPort.GetPortNames();
            PortNames = new List<string>(portNames);
        }

        /// <summary>
        /// 初始化波特率列表
        /// </summary>
        private void InitBaudRateTypes()
        {
            BaudRateTypes = new List<int>()
            {
                300,600,1200,2400,4800,9600,14400,19200,38400,56000,57600,115200
            };
        }

        /// <summary>
        /// 初始化校验类型
        /// </summary>
        private void InitParityTypes()
        {
            ParityTypes = new Dictionary<string, Parity>();
            Type type = typeof(Parity);
            foreach (var parity in Enum.GetValues<Parity>())
            {
                string name = Enum.GetName(type, parity);
                ParityTypes.Add(name,parity);
            }
        }

        /// <summary>
        /// 初始化数据位
        /// </summary>
        private void InitDataBitTypes()
        {
            DataBitTypes = new List<int>() { 5, 7, 8 };
        }

        /// <summary>
        /// 初始化停止位
        /// </summary>
        private void InitStopBitTypes()
        {
            StopBitTypes = new Dictionary<string, StopBits>();
            Type type = typeof(StopBits);
            foreach (var stopBit in Enum.GetValues<StopBits>())
            {
                string? name = Enum.GetName(type,stopBit);
                StopBitTypes.Add(name, stopBit);
            }
        }

        /// <summary>
        /// 初始化编码方式
        /// </summary>
        private void InitEncodingTypes()
        {
            EncodingTypes = new Dictionary<string, Encoding>();
            EncodingTypes.Add("UTF-8", Encoding.UTF8);
            EncodingTypes.Add("ASCII", Encoding.ASCII);
            EncodingTypes.Add("Latin1", Encoding.Latin1);
        }

        /// <summary>
        /// 初始化时间单位类型
        /// </summary>
        private void InitTimeUnitTypes()
        {
            TimeUnitTypes = new List<TimeUnit>(){
                new TimeUnit(){ Name="毫秒",Value=1},
                new TimeUnit(){ Name="秒",Value= 1000},
                new TimeUnit(){ Name="分钟", Value = 60 * 1000 },
                new TimeUnit(){ Name="小时", Value = 60 * 60 * 1000}
            };

        }
        #endregion
    }

    public class TimeUnit
    {
        public string Name { get; set; }
        public int Value { get; set; }
    }
}
